Skip to content

ci: harden GitHub Actions workflows with zizmor#1623

Merged
wochinge merged 2 commits intomainfrom
harden-gh-actions
Apr 15, 2026
Merged

ci: harden GitHub Actions workflows with zizmor#1623
wochinge merged 2 commits intomainfrom
harden-gh-actions

Conversation

@wochinge
Copy link
Copy Markdown
Contributor

@wochinge wochinge commented Apr 14, 2026

Summary

  • Add permissions: {} defaults to workflows missing explicit permissions
  • Add persist-credentials: false to all actions/checkout steps
  • Move ${{ }} interpolations in run: blocks to env: vars (template injection prevention)
  • Replace softprops/action-gh-release with gh release create
  • Switch claude-review-maintainer-prs from pull_request_target to pull_request with fork exclusion
  • Replace spoofable github.actor check with user.id for dependabot auto-merge
  • Add zizmor CI workflow for ongoing monitoring
  • Disable uv cache in release workflow (publishes to PyPI)
  • Add lookup-only: true to mypy cache

Test plan

  • CI passes (linting, type-checking, unit tests, e2e tests)
  • zizmor workflow runs clean
  • Verify next release workflow still works (persist-credentials + gh auth setup-git)
  • add branch protection rule for high severity errors

Fixes https://linear.app/langfuse/issue/LFE-9208/zizmor-python-sdk

🤖 Generated with Claude Code

Disclaimer: Experimental PR review

Greptile Summary

This PR hardens all GitHub Actions workflows against common CI/CD security vulnerabilities identified by zizmor: template injection in run: blocks, over-privileged workflows, spoofable actor checks, and the pull_request_target privilege escalation pattern. The changes are well-scoped and follow established hardening best practices.

  • The mypy cache in ci.yml now has lookup-only: true which disables both restore and save, making the cache step a no-op. The # zizmor: ignore[cache-poisoning] comment on the same line already suppresses the linter warning and is sufficient on its own.

Confidence Score: 5/5

  • Safe to merge — all findings are P2 style/performance suggestions with no correctness or security impact.
  • The single finding (disabled mypy cache) only affects CI performance, not correctness or security. All security hardening changes are correct and well-implemented.
  • ci.yml — the lookup-only: true on the mypy cache step.

Important Files Changed

Filename Overview
.github/workflows/ci.yml Added permissions: {}, persist-credentials: false on all checkouts, and uv cache-poisoning ignore comments. The lookup-only: true on the mypy cache step effectively disables cache restore and save, making it a no-op.
.github/workflows/release.yml All ${{ }} interpolations in run: blocks moved to env: vars. Replaced softprops/action-gh-release with gh release create. Added persist-credentials: false alongside the existing gh auth login + gh auth setup-git pattern — correct approach for push credentials.
.github/workflows/claude-review-maintainer-prs.yml Switched from pull_request_target to pull_request and added fork-exclusion guard — eliminates the privileged-context risk for untrusted fork PRs.
.github/workflows/dependabot-merge.yml Replaced spoofable github.actor == 'dependabot[bot]' with github.event.pull_request.user.id == 49699333 — a hardened check that can't be bypassed by renaming a user account.
.github/workflows/zizmor.yml New workflow for ongoing zizmor CI monitoring. Correctly scoped to same-repo PRs only, with pinned action SHA and minimal permissions.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[release.yml triggered] --> B[Verify branch via GITHUB_REF env]
    B --> C[Confirm major via INPUTS env]
    C --> D[checkout with persist-credentials false]
    D --> E[gh auth login + setup-git\nrestore push credentials]
    E --> F[Calculate version\nall inputs via env vars]
    F --> G[uv build]
    G --> H[git commit + push\nvia gh credential helper]
    H --> I[uv publish to PyPI\nOIDC trusted publishing]
    I --> J[gh release create\nreplaces softprops action]

    K[claude-review workflow] --> L{pull_request trigger\nnot pull_request_target}
    L --> M{head.repo == repository?}
    M -->|same repo| N[Post claude review comment]
    M -->|fork| O[Skip - untrusted context]

    P[dependabot-merge] --> Q{user.id check\nnot actor name}
    Q -->|match| R[Auto-merge patch updates]
    Q -->|no match| S[Skip]
Loading

Reviews (1): Last reviewed commit: "ci: harden GitHub Actions workflows with..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

@github-actions
Copy link
Copy Markdown

@claude review

@github-advanced-security
Copy link
Copy Markdown

You are seeing this message because GitHub Code Scanning has recently been set up for this repository, or this pull request contains the workflow file for the Code Scanning tool.

What Enabling Code Scanning Means:

  • The 'Security' tab will display more code scanning analysis results (e.g., for the default branch).
  • Depending on your configuration and choice of analysis tool, future pull requests will be annotated with code scanning analysis results.
  • You will be able to see the analysis results for the pull request's branch on this overview once the scans have completed and the checks have passed.

For more information about GitHub Code Scanning, check out the documentation.

Comment thread .github/workflows/ci.yml Fixed
Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml
- Add `permissions: {}` defaults to ci, dependabot-rebase-stale,
  package-availability-check workflows
- Add `persist-credentials: false` to all checkout steps
- Move `${{ }}` interpolations in run blocks to env vars (release.yml)
- Replace `softprops/action-gh-release` with `gh release create`
- Switch claude-review from `pull_request_target` to `pull_request`
  with fork check
- Replace spoofable `github.actor` check with `user.id` for dependabot
- Add zizmor CI workflow for ongoing monitoring
- Add `lookup-only: true` to mypy cache (type-checking job)
- Disable uv cache in release workflow (publishes to PyPI)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@wochinge wochinge force-pushed the harden-gh-actions branch from abc8ea5 to a6b9fec Compare April 14, 2026 14:01
Copy link
Copy Markdown

@claude claude bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The lookup-only: true issue I flagged previously is no longer present in the current diff — LGTM on the security hardening.

Extended reasoning...

Overview

This PR hardens all eight GitHub Actions workflow files against common CI/CD security vulnerabilities flagged by zizmor: adds permissions: {} defaults, persist-credentials: false on all checkouts, moves ${{ }} template interpolations to env: vars, replaces softprops/action-gh-release with gh release create, switches claude-review-maintainer-prs from pull_request_target to pull_request with fork exclusion, and replaces the spoofable github.actor check with a user.id comparison in dependabot-merge.yml. A new zizmor.yml workflow provides ongoing monitoring.

Security risks

The changes reduce risk rather than introduce it. The only remaining finding (noted as a nit inline) is a version comment inconsistency in the new zizmor.yml (# v6.0.2 vs # v6 used by every other workflow for the same SHA) — cosmetic only, no security impact.

Level of scrutiny

This is CI/CD configuration hardening with no changes to application code. The patterns used (persist-credentials: false, env: var injection, user.id checks) are well-established hardening practices. The release workflow changes are the most consequential (touching push/publish logic), but the approach — disabling uv cache, moving template interpolations to env:, and replacing the third-party release action with the GitHub CLI — is sound.

Other factors

My prior review flagged lookup-only: true on the mypy cache step as breaking incremental caching. The current diff no longer contains that setting, so that issue has been resolved. The remaining nit (version comment in zizmor.yml) is covered by the inline comment and does not warrant blocking the PR.

Comment thread .github/workflows/zizmor.yml Outdated
- zizmor.yml: align checkout comment to `# v6` matching other workflows
- ci.yml: fix actions/cache comment from `# v5` to `# v5.0.4` (actual tag)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@wochinge wochinge merged commit ffaaaa3 into main Apr 15, 2026
18 checks passed
@wochinge wochinge deleted the harden-gh-actions branch April 15, 2026 08:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants